package edu.wpi.cs509.representation;

import static org.junit.Assert.fail;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

/**
 * Action is the base class for actions that can be taken with pieces.
 * For example adding or removing pieces to/from the board,
 * moving pieces on the board, attacking (battling) one piece
 * by another.
 * 
 * Action takes a Gameboard, StrategoPiece, and Location
 * as part of the constructor.
 * 
 * Action was originally designed to capture the intent
 * of the move from the client, check the move against rules,
 * and execute the move to update gameboard at the server.
 * To this has been added support for an after-the-move use
 * whereby the UI uses it to find out what transpired so it can
 * update its presentation.
 * 
 * Action provides the following methods:
 * 
 * setGameboard() will reassign the gameboard.  This is used
 * when the action has been created by the client, but then
 * is sent to the server to be executed.  setGameboard
 * reassigns references for pieces and locations so they
 * refer to the server's gameboard, so that execute() will
 * update that gameboard.
 * 
 * check() checks the move for legality and throws an
 * actionNotLegalException if it is not legal. It does not
 * execute the transaction.  It executes against the
 * current gameboard reference, so can be run by client
 * or server.
 * 
 * execute() runs check() and then performs the action.
 * By performing the action Action updates the gameboard.
 * 
 * Exceptions for Action class:
 * ActionNotLegalException indicates the action was
 * inconsistent with the Stratego rules such as an
 * attempt to move the flag.
 * Upon receiving this exception the caller should expect
 * the action was rejected (not performed).
 * 
 * ActionEndOfGameException indicates the action
 * resulted in the taking of the flag so the game is
 * over.
 * 
 */
public abstract class ActionBaseClass extends StrategoObjectBc {

	/**
	 * parameters of ActionBaseClass
	 */
	private static final long serialVersionUID = 1L;
	protected Gameboard gameboard; // a reference to the game board

	protected PlayerPiece pieceBeingMoved; // the piece being moved
	protected Location pieceBeingMovedOriginalSourceLocation; // original source location of piece being moved
	protected Location pieceBeingMovedOriginalDestinationLocation; // original destination of piece being moved
	private boolean myIsAnAttack; // whether this is/was an attack
	private PlayerPiece pieceBeingAttacked;

	protected String errMessage; // a message relating to the error

	// historical record of the winning and losing pieces
	// in the case of a draw there will be two losing pieces and no winning pieces
	// in the case of a non-battle (regular move) there will be no winning or losing pieces
	private Queue<PlayerPiece> historicalWinningPiecesList =  new LinkedList<PlayerPiece>();
	private Queue<PlayerPiece> historicalLosingPiecesList  =  new LinkedList<PlayerPiece>();

	private PlayerPiece uiAttacker;
	private PlayerPiece uiDefender;


	/**
	 * Constructor
	 * @param name - name for the action
	 * @param gameboard - gameboard on which the action is to be performed
	 * @param pce - the piece taking the action
	 * @param loc - the destination location for the piece
	 */
	public ActionBaseClass(String name, Gameboard gb,
			PlayerPiece pce, Location loc) {

		super(name);
		this.gameboard = gb;
		this.pieceBeingMoved = pce;
		this.pieceBeingMovedOriginalSourceLocation = pieceBeingMoved.getLocation();
		this.pieceBeingMovedOriginalDestinationLocation = loc;

		// get reference to the defending piece
		// or null if there is not a piece there (not an attack)
		pieceBeingAttacked = null;
		try {
			pieceBeingAttacked = gameboard.getPlayerPieceOnBoardSquare(
					pieceBeingMovedOriginalDestinationLocation.getXCoordinate(),
					pieceBeingMovedOriginalDestinationLocation.getYCoordinate());
		}
		catch (GameboardException e) {
			System.out.println("- saw a gameboard exception - X="+pieceBeingMovedOriginalDestinationLocation.getXCoordinate()+" Y="+pieceBeingMovedOriginalDestinationLocation.getYCoordinate());
			e.printStackTrace();
			fail("112233.1 - caught an exception");
		}


		this.setErrMessage("no error");

		Logger.getLogger("global").setLevel(Level.FINEST);
		//logger.severe("this is severe");
		//logger.warning("this is warning");
		//Logger.getLogger("global").info("this is info");
		//logger.fine("this is fine");
		//logger.finer("this is finer");
		//logger.finest("this is finest");


		// sanity check
		if ((gameboard == null) ||
				(pieceBeingMoved == null) ||
				(pieceBeingMovedOriginalSourceLocation == null) ||
				(pieceBeingMovedOriginalDestinationLocation == null)) {
			Logger.getLogger("global").severe("--------- we have a BIG problem here - null pointer sent to Action -----");
			// throw an exception
			throw new NullPointerException();
		}

		// squirrel away isAnAttack
		myIsAnAttack = partOnTheDestination();
	}


	/**
	 * associate the action with an alltogether new gameboard
	 * this basically involves finding the equivalent reference
	 * in the new gameboard
	 * @param gb - the new gameboard
	 */
	public void setGameboard(Gameboard newGameboard) {

		PlayerPiece pieceBeingMoved_newGameboard = null; // the piece to be moved (on new gameboard)
		Location    pieceBeingMovedOriginalSourceLocation_newGameboard = null; // the source location (on new gameboard)
		Location    pieceBeingMovedOriginalDestinationLocation_newGameboard = null; // the destination location (on new gameboard)
		PlayerPiece pieceBeingAttacked_newGameboard; // piece being attacked
		Location    pieceBeingAttackedLocation_newGameboard = null; // location of attack-ee (on new gameboard)

		// find equivalent source location on new gameboard
		if (pieceBeingMovedOriginalSourceLocation.isInBoneyard()) {
			pieceBeingMovedOriginalSourceLocation_newGameboard =  newGameboard.getBoneyard();
		}
		else try {
			pieceBeingMovedOriginalSourceLocation_newGameboard = newGameboard.getBoardsquareFromIndex(pieceBeingMovedOriginalSourceLocation.getIndex());
		} catch (GameboardException e) {
			Logger.getLogger("global").severe("caught exception when mapping to newSourceLocation");
			e.printStackTrace();
		}

		// find equivalent destination location on new gameboard
		if (pieceBeingMovedOriginalDestinationLocation.isInBoneyard()) {
			pieceBeingMovedOriginalDestinationLocation_newGameboard =  newGameboard.getBoneyard();
		}
		else try {
			pieceBeingMovedOriginalDestinationLocation_newGameboard = newGameboard.getBoardsquareFromIndex(pieceBeingMovedOriginalDestinationLocation.getIndex());
		} catch (GameboardException e) {
			Logger.getLogger("global").severe("caught exception when mapping to newDestinationLocation");
			e.printStackTrace();
		}

		// find equivalent piece being moved on new gameboard
		// if the source is the boneyard get any piece of that type
		// if the source is on the board get the piece based on location index
		if (pieceBeingMovedOriginalSourceLocation.isInBoneyard()) {
			pieceBeingMoved_newGameboard = newGameboard.getPlayerPieceInBoneyard(pieceBeingMoved.getColor(), pieceBeingMoved.getPieceType());
		}		
		else try {
			pieceBeingMoved_newGameboard = newGameboard.getPlayerPieceAtGameboardIndex(pieceBeingMovedOriginalSourceLocation_newGameboard.getIndex());
		} catch (GameboardException e) {
			Logger.getLogger("global").severe("caught exception when mapping to newPieceBeingMoved");
			e.printStackTrace();
		}

		//////////////////////
		// piece being attacked...
		pieceBeingAttacked_newGameboard = null;

		// find equivalent location
		// if the piece being attacked was null this indicates this was not an attack
		if (pieceBeingAttacked != null) {
			try {
				pieceBeingAttackedLocation_newGameboard = newGameboard.getBoardsquareFromIndex(pieceBeingAttacked.getLocation().getIndex());
			} catch (GameboardException e) {
				Logger.getLogger("global").severe("caught exception when mapping attack-ee location");
				e.printStackTrace();
			}

			// attackee piece
			try {
				pieceBeingAttacked_newGameboard = newGameboard.getPlayerPieceAtGameboardIndex(pieceBeingAttackedLocation_newGameboard.getIndex());
			} catch (GameboardException e) {
				Logger.getLogger("global").severe("caught exception when mapping to new attack-ee piece");
				e.printStackTrace();
			}
		}



		// switch over to the new gameboard, locations, piece
		this.pieceBeingMovedOriginalSourceLocation = pieceBeingMovedOriginalSourceLocation_newGameboard;
		this.pieceBeingMovedOriginalDestinationLocation = pieceBeingMovedOriginalDestinationLocation_newGameboard;
		this.pieceBeingMoved = pieceBeingMoved_newGameboard;
		this.pieceBeingAttacked = pieceBeingAttacked_newGameboard;
		this.gameboard = newGameboard;

		// sanity checks
		// throw an exception if we have generated a null
		if (gameboard == null) {
			Logger.getLogger("global").severe("--------- BIG problem mapping client move to server gameboard - results in null gameboard");
			throw new NullPointerException();
		}
		if (pieceBeingMovedOriginalSourceLocation == null) {
			Logger.getLogger("global").severe("--------- BIG problem mapping client move to server gameboard - results in null sourceLocation");
			throw new NullPointerException();
		}
		if (pieceBeingMovedOriginalDestinationLocation == null) {
			Logger.getLogger("global").severe("--------- BIG problem mapping client move to server gameboard - results in null destinationLocation");
			throw new NullPointerException();
		}
		if (pieceBeingMoved == null) {
			Logger.getLogger("global").severe("--------- BIG problem mapping client move to server gameboard - results in null pieceBeingMoved");
			throw new NullPointerException();	
		}
	}


	/** strategoRulesCheck()
	 * checks the move for legality per the Stratego rules
	 * Throws an exception in case it is not legal.
	 * Does NOT update the gameboard.
	 * check() is a non-abstract FINAL method.  It represents the
	 * external interface to the class.  It calls doCheck()
	 * which is defined by the derived class
	 * @throws ActionNotLegalException
	 */
	public final void strategoRulesCheck() throws ActionNotLegalException {

		// debug message
		Logger.getLogger("global").info("here");

		// perform the checking
		try {
			doStrategoRulesCheck();
		}
		finally {
			Logger.getLogger("global").info("message from strategoRulesCheck... " + getErrMessage());
		}
	}


	/** execute()
	 * performs the action and updates the gameboard
	 * checking is performed and an exception will be thrown if
	 * execute() finds something illegal about the action.  In that event
	 * the caller should expect the action has been rejected (not taken).
	 * An exception is thrown if the move results in the game being won (ended).
	 * @throws ActionNotLegalException - illegal action
	 * @throws ActionEndOfGameException - move results in end of game
	 */
	public final void execute() throws ActionNotLegalException, ActionEndOfGameException {

		// debug message
		Logger.getLogger("global").info("here");

		// rules checking
		strategoRulesCheck();

		// perform the action
		doExecute();
	}


	/** executeAgainstGameboard()
	 * performs the action against, specifying the gameboard to be updated
	 * @param altGb - the gameboard on which the action should be executed
	 * @throws ActionNotLegalException - exception if move is not legal
	 * @throws ActionEndOfGameException - exception if move ends the game
	 */
	/*
	public final void executeAgainstGameboard(Gameboard altGb) throws ActionNotLegalException, ActionEndOfGameException {

		// debug message
		Logger.getLogger("global").info("here");

		// change the gameboard
		this.gameboard = altGb;

		// execute the action
		this.execute();
	}
	 */

	/** doStrategoRulesCheck()
	 * is derived-class version
	 * actually does the checking.
	 * is abstract - the derived class must implement
	 * @throws ActionNotLegalException
	 */
	abstract protected void doStrategoRulesCheck() throws ActionNotLegalException;

	/**
	 * getHistoricalWinningPiecesList
	 * Returns a Queue which holds the list of winning pieces for this move
	 * The queue will be zero or one item in length depending on whether
	 * the was a battle and the result of that battle
	 * @return Queue of PlayerPiece which empty or has the winning piece
	 */
	public Queue<PlayerPiece> getHistoricalWinningPiecesList() {
		return historicalWinningPiecesList;
	}

	/**
	 * getHistoricalLosingPiecesList
	 * Returns a Queue which holds the list of losing pieces for this move
	 * The queue will be zero, one, or two items in length depending on
	 * whether there was a battle and the result of the battle.  If the
	 * battle ended up a draw both pieces lose and the Queue will have 2
	 * pieces
	 * @return Queue of PlayerPiece which has zero, one or two pieces
	 */
	public Queue<PlayerPiece> getHistoricalLosingPiecesList() {
		return historicalLosingPiecesList;
	}


	/**
	 * set a message describing the error case
	 * @param msg - the descriptive message
	 */
	void setErrMessage(String msg) {
		this.errMessage = msg;
	}

	/**
	 * get the message describing the error case
	 * @return - the message
	 */
	public String getErrMessage() {
		return errMessage;
	}

	/**
	 * toString - return a string with the object's contents
	 */
	public String toString() {
		StringBuilder result = new StringBuilder();
		String NEW_LINE = System.getProperty("line.separator");

		result.append(this.getClass().getName() + " Object {" + NEW_LINE);
		result.append(" pieceBeingMoved"+pieceBeingMoved.toString() + NEW_LINE);
		result.append(" pieceBeingMovedOriginalSourceLocation "+pieceBeingMovedOriginalSourceLocation.toString() + NEW_LINE);
		result.append(" pieceBeingMovedOriginalDestinationLocation "+pieceBeingMovedOriginalDestinationLocation.toString() + NEW_LINE);
		result.append(" myIsAnAttack "+myIsAnAttack + NEW_LINE);
		if (myIsAnAttack) {
			result.append(" pieceBeingAttacked "+pieceBeingAttacked.toString() + NEW_LINE);
		}
		result.append(" errMessage"+errMessage + NEW_LINE);
		result.append("}");

		return result.toString();
	}



	///////////////////////////////////
	////// movement rules checking ////
	///////////////////////////////////


	/**
	 * isBoneyardDestination
	 * see if destination is boneyard
	 * @return true if destination is the boneyard
	 */
	protected boolean isBoneyardDestination() {
		return (pieceBeingMovedOriginalDestinationLocation == gameboard.getBoneyard());
	}


	/**
	 * isOccupiableDestination
	 * see if destination is occupiable (not lake)
	 * @return true if destination is occupiable (not lake)
	 */
	protected boolean isOccupiableDestination() {
		return (pieceBeingMovedOriginalDestinationLocation.isOccupiable());
	}


	/**
	 * destinationIsOnMyHalfOfBoard
	 * see if destination is on "my" half of the board
	 * uses the moving piece's color to determine which half
	 * @return true if destination on "my" half of the board
	 */
	protected boolean destinationIsOnMyHalfOfBoard() {
		TeamColor col = pieceBeingMoved.getColor();

		Logger.getLogger("global").info("(col == RED) " + (col == TeamColor.RED) + " X " + pieceBeingMovedOriginalDestinationLocation.getXCoordinate() + " Y " + pieceBeingMovedOriginalDestinationLocation.getYCoordinate());

		return ((col == TeamColor.RED) ?
				(pieceBeingMovedOriginalDestinationLocation.getYCoordinate() <= 3) :
					(pieceBeingMovedOriginalDestinationLocation.getYCoordinate() >= 6));
	}


	/**
	 * partOnTheDestination
	 * see if there is a part on the destination
	 * @return true if there is a part on the destination
	 */
	private boolean partOnTheDestination() {
		try {
			if (pieceBeingMovedOriginalDestinationLocation.isInBoneyard())
				return (false);
			else  {
				PlayerPiece pce = gameboard.getPlayerPieceOnBoardSquare(
						pieceBeingMovedOriginalDestinationLocation.getXCoordinate(),
						pieceBeingMovedOriginalDestinationLocation.getYCoordinate());
				// return false if the location is empty
				return (pce != null);
			}
		} catch (GameboardException e) {
			System.out.println("error - exception caught in partOnTheDestination");
			e.printStackTrace();
		}
		return true; // unused
	}


	/**
	 * destinationIsTheSameAsCurrentLocation
	 * check if the destination is the same as the current
	 * location.  This would be a non-move
	 * @return true if the destination is the same as the current location
	 */
	protected boolean destinationIsTheSameAsCurrentLocation() {
		return (pieceBeingMovedOriginalDestinationLocation == pieceBeingMoved.location);
	}

	/**
	 * yDistance
	 * @return an integer with the absolute value of
	 * the y distance to be moved
	 */
	private int yDistance() {
		return Math.abs(pieceBeingMoved.location.getYCoordinate() -
				pieceBeingMovedOriginalDestinationLocation.getYCoordinate());
	}

	/**
	 * xDistance
	 * @return an integer with the absolute value of
	 * the x distance to be moved
	 */
	private int xDistance() {
		return Math.abs(pieceBeingMoved.location.getXCoordinate() -
				pieceBeingMovedOriginalDestinationLocation.getXCoordinate());
	}

	/**
	 * movementIsStrictlyXorY - checks the movement to
	 * see if it is exclusively x or y (not a combination)
	 * @return a boolean true if it is strictly x or y
	 */
	protected boolean movementIsStrictlyYorX() {
		return((yDistance()==0) || (xDistance()==0));
	}

	/**
	 * partCanMoveThatNumberOfSpaces - checks is a part
	 * can move the number of spaces requested
	 * @return a boolean true if the part can move the
	 * number of spaces requested
	 */
	protected boolean partCanMoveThatNumberOfSpaces() {
		return ((pieceBeingMoved.maxSpacesCanMove() >= xDistance()) &&
				(pieceBeingMoved.maxSpacesCanMove() >= yDistance()));
	}

	/**
	 * isaMultiSquareMove - checks if the move is more than one
	 * square in distance (a scout can do this)
	 * @return boolean if the move is multi-square
	 */
	private boolean isaMultiSquareMove() {
		return ((yDistance() > 1) ||
				(xDistance() > 1));
	}

	/**
	 * getYValues - get the list of y values associated with
	 * a multi-square move.
	 * @return - returns Set<Integer> which is the set of
	 * y values in the path
	 */
	private Set<Integer> getYvalues() {
		Set<Integer> theVals = new HashSet<Integer>();

		if (pieceBeingMoved.location.getYCoordinate() <= pieceBeingMovedOriginalDestinationLocation.getYCoordinate())
			for (int i=pieceBeingMoved.location.getYCoordinate()+1;
					i<=pieceBeingMovedOriginalDestinationLocation.getYCoordinate();
					i++)
				theVals.add(i);
		else
			for (int i=pieceBeingMoved.location.getYCoordinate()-1;
					i>=pieceBeingMovedOriginalDestinationLocation.getYCoordinate();
					i--)
				theVals.add(i);
		return theVals;
	}

	/**
	 * getXvalues - get the list of x values associated with
	 * a multi-square move.
	 * @return - returns Set<Integer> which is the set of
	 * x values in the path
	 */
	private Set<Integer> getXvalues() {
		Set<Integer> theVals = new HashSet<Integer>();

		if (pieceBeingMoved.location.getXCoordinate() <= pieceBeingMovedOriginalDestinationLocation.getXCoordinate())
			for (int i=pieceBeingMoved.location.getXCoordinate()+1;
					i<=pieceBeingMovedOriginalDestinationLocation.getXCoordinate();
					i++)
				theVals.add(i);
		else
			for (int i=pieceBeingMoved.location.getXCoordinate()-1;
					i>=pieceBeingMovedOriginalDestinationLocation.getXCoordinate();
					i--)
				theVals.add(i);
		return theVals;
	}

	/**
	 * multiSquareMovePiecesAlongTheWay - for a part that
	 * can move more than one space at a time (the scout)
	 * this method checks to see if another piece
	 * is along the path.
	 *
	 * Note also that a Scout cannot attack from more than
	 * one spot away. This function checks for that case
	 * also.
	 * @return a boolean true if there is a
	 * piece intervening
	 */
	protected boolean multiSquareMovePiecesAlongTheWay() {

		// none of this applies if it is not multi-square
		if (!isaMultiSquareMove()) {
			return false;
		}

		for (Integer x :  getXvalues()) {
			for (Integer y : getYvalues()) {
				System.out.println("zona - checking x,y"+x+" "+y);
				if ( partOnXY(x,y) ) {
					System.out.println("zona - is bad");
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * partOnYX
	 * see if there is a part on the [y,x] location
	 * @return true if there is a part on the destination
	 */
	protected boolean partOnXY(int x, int y) {
		try {
			PlayerPiece pce = gameboard.getPlayerPieceOnBoardSquare(x,y);
			// return false if the location is empty
			return (pce != null);
		} catch (GameboardException e) {
			System.out.println("error - exception caught in partOnXY");
			e.printStackTrace();
		}
		return true; // unused
	}

	/**
	 * multiSquareMoveSwampAlongTheWay - for a part that
	 * can move more than one space at a time (the scout)
	 * this method checks to see if a swamp
	 * is along the path.
	 *
	 * @return a boolean true if there is a
	 * swamp intervening
	 */
	protected boolean multiSquareMoveSwampAlongTheWay() {

		// none of this applies if it is not multi-square
		if (!isaMultiSquareMove()) {
			return false;
		}

		for (Integer x :  getXvalues()) {
			for (Integer y : getYvalues()) {
				if (isOccupiableXY(x,y)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * isOccupiableYX
	 * see if destination [X,Y] is occupiable (not lake)
	 * @return true if destination is occupiable (not lake)
	 */
	protected boolean isOccupiableXY(Integer x, Integer y) {
		try {
			return gameboard.getBoardsquare(x, y).isOccupiable();
		} catch (GameboardException e) {
			System.out.println("error - GameboardException caught in isOccupiableRowColumn");
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * multiSquareAttack - for a part that can move
	 * more than one space at a time (the scout)
	 * this method checks to see if the multi-square
	 * move lands (attacks) an opposing piece.
	 *
	 * @return a boolean true if this is a multi-sqare
	 * move which is ends in an attack
	 */
	protected boolean multiSquareAttack() {

		// none of this applies if it is not multi-square
		return isaMultiSquareMove() && isAnAttack();
	}

	/**
	 * isAnAttack - checks if the move ends in an attack
	 * @return boolean true if the move is an attack
	 */
	public boolean isAnAttack() {
		return myIsAnAttack;
	}




	///////////////////////////////////
	////// battle rules checking //////
	///////////////////////////////////

	/**
	 * canAttack - checks if the piece can
	 * perform an attack
	 * @return boolean true if piece can attack
	 */
	protected boolean canAttack() {
		return pieceBeingMoved.maxSpacesCanMove() > 0;
	}

	/**
	 * attackerWins - determines if the attacking piece wins the battle
	 * @return boolean indicating the attacker wins
	 */
	protected boolean attackerWins() {
		PlayerPiece the_defender = getPieceBeingAttacked();

		// check if attacking a bomb but the piece can kill bombs
		if (pieceBeingMoved.canKillaBomb() && the_defender.isaBomb()) {
			return true;
		}

		// check if attacking a marshall but the piece can kill marshalls
		if (pieceBeingMoved.canKillaMarshal() && the_defender.isaMarshal()) {
			return true;			
		}

		// compare rank - the lower rank is more powerful and wins
		if (pieceBeingMoved.getRank() < the_defender.getRank()) {
			return true;
		}
		return false;	
	}

	/**
	 * defenderWins - determines if the defending piece wins the battle
	 * @return boolean indicating the defender wins
	 */
	protected boolean defenderWins() {
		PlayerPiece the_defender = getPieceBeingAttacked();

		// check if attacking a bomb - result is determined if the attacker can kill a bomb
		if (the_defender.isaBomb()) {
			return !pieceBeingMoved.canKillaBomb();
		}

		// compare rank - the lower rank is more powerful and wins
		if (the_defender.getRank() < pieceBeingMoved.getRank()) {
			return true;
		}
		return false;	
	}




	/** doExecute()
	 * doExecute() is the derived-class version of execute() which
	 * actually does the execute.
	 * doExecute is abstract - the derive class must implement
	 * @throws ActionNotLegalException - illegal action
	 * @throws ActionEndOfGameException - move results in end of game
	 */
	protected void doExecute() throws ActionNotLegalException, ActionEndOfGameException {

		Logger.getLogger("global").info("ActionBaseClass.doExecute() isAnAttack "+isAnAttack());

		// if it is not an attack
		// the moving piece goes to the location
		if (!isAnAttack()) {

			pieceBeingMoved.setLocation(pieceBeingMovedOriginalDestinationLocation);
		}

		// if it is an attack...
		else {

			Logger.getLogger("global").info("attackerWins "+attackerWins()+" defenderWins "+defenderWins());
			Logger.getLogger("global").info("destinationLocation = "+pieceBeingMovedOriginalDestinationLocation.getYCoordinate()+pieceBeingMovedOriginalDestinationLocation.getXCoordinate());

			// make a copy of attacker and defender for UI (unobfuscated piece)
			 uiAttacker = new PlayerPiece(
					pieceBeingMoved.pieceType,
					pieceBeingMoved.color,
					pieceBeingMoved.getLocation() );
			 uiDefender = new PlayerPiece(
					getPieceBeingAttacked().pieceType,
					getPieceBeingAttacked().getColor(),
					getPieceBeingAttacked().getLocation());


			// if attacker wins
			// defending piece goes to the boneyard
			// attacking piece moves to the destined location
			if (attackerWins()) {
				// squirrel away the winning, losing pieces
				historicalWinningPiecesList.add(pieceBeingMoved);
				historicalLosingPiecesList.add(getPieceBeingAttacked());

				// note if this will end the game
				boolean thisWillEndGame = (getPieceBeingAttacked().getPieceType()==StrategoPiece.FLAG);

				// defending piece goes to the boneyard
				// attacking piece moves to the destined location
				getPieceBeingAttacked().setLocation(gameboard.getBoneyard());
				uiDefender.setLocation(gameboard.getBoneyard());
				Logger.getLogger("global").info("destination_location "+pieceBeingMovedOriginalDestinationLocation.getYCoordinate()+pieceBeingMovedOriginalDestinationLocation.getXCoordinate());
				pieceBeingMoved.setLocation(pieceBeingMovedOriginalDestinationLocation);
				uiAttacker.setLocation(pieceBeingMovedOriginalDestinationLocation);
				Logger.getLogger("global").info("pieceBeingMoved " + pieceBeingMoved.getLocation().getYCoordinate()+pieceBeingMoved.getLocation().getXCoordinate());

				// if the losing defender is the flag - throw a game is won exception
				if (thisWillEndGame) {
					Logger.getLogger("global").info("---> flag has been captured throwing ActionEndOfGameException");
					throw new ActionEndOfGameException( pieceBeingMoved );
				}
			}

			// if defender wins
			// attacking piece goes to the boneyard
			else if (defenderWins()) {
				// squirrel away the winning, losing pieces
				historicalWinningPiecesList.add(getPieceBeingAttacked());
				historicalLosingPiecesList.add(pieceBeingMoved);

				// move the losing piece to the boneyard		
				pieceBeingMoved.setLocation(gameboard.getBoneyard());
				uiAttacker.setLocation(gameboard.getBoneyard());
			}

			// a draw - they both lose and go to the boneyard
			else {
				// squirrel away the losing pieces
				historicalLosingPiecesList.add(pieceBeingMoved);
				historicalLosingPiecesList.add(getPieceBeingAttacked());

				// move the pieces to the boneyard
				pieceBeingAttacked.setLocation(gameboard.getBoneyard());
				uiDefender.setLocation(gameboard.getBoneyard());
				pieceBeingMoved.setLocation(gameboard.getBoneyard());
				uiAttacker.setLocation(gameboard.getBoneyard());
			}

		}
	}


	/**
	 * getPieceBeingMoved
	 * @return PlayerPiece of the piece being moved
	 */
	public PlayerPiece getPieceBeingMoved() {
		return this.pieceBeingMoved;
	}

	/**
	 * getPieceBeingMovedOriginalSourceLocation
	 * @return original "from" Location of the piece being moved
	 */
	public Location getPieceBeingMovedOriginalSourceLocation() {
		return this.pieceBeingMovedOriginalSourceLocation;
	}

	/**
	 * getPieceBeingMovedOriginalDestinationLocation
	 * @return original "to" Location of the piece being moved
	 * note this may 
	 */
	public Location getPieceBeingMovedOriginalDestinationLocation() {
		return this.pieceBeingMovedOriginalDestinationLocation;
	}

	/**
	 * getPieceBeingAttacked
	 *  * @return PlayerPiece the piece being attacked, or null
	 */
	public PlayerPiece getPieceBeingAttacked() {
		return pieceBeingAttacked;
	}


	public PlayerPiece getUiAttacker() {
		return uiAttacker;
	}



	public PlayerPiece getUiDefender() {
		return uiDefender;
	}



}
